#ifndef MPQC_RANGE_HPP
#define MPQC_RANGE_HPP

#include <iostream>

//#include <boost/iterator/iterator_facade.hpp>

namespace mpqc {

//  struct range_block_iterator;

//  template<class It>
//  class iterator_range {
//  private:
//    It begin_;
//    It end_;
//  public:
//    typedef typename std::iterator_traits<It>::difference_type difference_type;
//
//    iterator_range(It begin, It end) : begin_(begin), end_(end) {}
//    std::size_t size() const { return std::distance(begin_, end_); }
//    It begin() const { return begin_; }
//    It end() const { return end_; }
//  };

  class range {
  public:

    typedef int value_type;
  private:
    value_type begin_;
    value_type end_;

  public:

    // Integral iterator object
    class iterator {
    public:
      // Iterator typedefs
      typedef range::value_type value_type;
      typedef std::ptrdiff_t difference_type;
      typedef value_type* pointer;
      typedef value_type reference;
      typedef std::random_access_iterator_tag iterator_category;

    private:
      const range* range_; // Pointer to the parent range object (differentiate iterators to different ranges)
      value_type value_; // The current value of the iterator

    public:

      // Constructors

      iterator() : range_(NULL), value_(0) { }

      iterator(const range* r, const value_type v) : range_(r), value_(v) { }

      iterator(const iterator& other) :
        range_(other.range_), value_(other.value_)
      { }

      // Assignment operators

      iterator& operator=(const iterator& other) {
        range_ = other.range_;
        value_ = other.value_;
        return *this;
      }

      iterator& operator+=(const std::size_t n) {
        value_ += n;
        return *this;
      }

      iterator& operator-=(const std::size_t n) {
        value_ -= n;
        return *this;
      }

      reference operator*() const { return value_; }

      // Increment/decrement operators

      iterator& operator++() {
        ++value_;
        return *this;
      }

      iterator operator++(int) {
        iterator temp(*this);
        ++value_;
        return temp;
      }

      iterator& operator--() {
        --value_;
        return *this;
      }

      iterator operator--(int) {
        iterator temp(*this);
        --value_;
        return temp;
      }

      // Offset dereference operator
      reference operator[](std::size_t n) const {
        return value_ + n;
      }

      // Advance operator
      iterator operator+(std::size_t n) const {
        return iterator(range_, value_ + n);
      }

      // Difference operator
      iterator operator-(const std::size_t n) const {
        return iterator(range_, value_ - n);
      }

      iterator operator-(const iterator& other) const {
        assert(range_ != NULL);
        assert(range_ == other.range_);
        return iterator(range_, value_ - other.value_);
      }

      // Comparison operator
      bool operator==(const iterator& other) const {
        return (((range_ == other.range_) && (range_ != NULL)) && (value_ == other.value_));
      }
      bool operator!=(const iterator& other) const { return ! operator==(other); }
      bool operator<(const iterator& other) const {
        return (((range_ == other.range_) && (range_ != NULL)) && (value_ < other.value_));
      }
      bool operator >(const iterator& other) const { return (other < *this); }
      bool operator <=(const iterator& other) const { return !(other < *this); }
      bool operator >=(const iterator& other) const { return !(*this < other); }
    }; // class range

    explicit range(int size = 0) : begin_(0), end_(size) {}

    range(int begin, int end) : begin_(begin), end_(end) {}

    int size() const { return end_ - begin_; }

    iterator begin() const { return iterator(this, begin_); }

    iterator end() const { return iterator(this, end_); }

    bool operator==(const range &r) const {
      return ((r.begin_ == begin_) && (r.end_ == end_));
    }

    static range intersection(const range &a, const range &b) {
      int begin = std::max(*a.begin(), *b.begin());
      int end = std::min(*a.end(), *b.end());
      return (begin < end) ? range(begin, end) : range();
    }

//    iterator_range<range_block_iterator> block(size_t N) const;

//    static std::vector<range> block(range r, size_t N) {
//      std::vector<range> blocks;
//      for (int i = *r.begin(); i < *r.end(); i += N) {
//        blocks.push_back(range(i, std::min<int>(i+N, *r.end())));
//      }
//      return blocks;
//    }

    std::vector<range> split2(size_t n) const {
      std::vector<range> v;
      int m = this->size()%n;
      int b = this->size()/n;
      int r = 0;
      for (int i = 0; i < m; ++i) {
        v.push_back(range(r, r+b+1));
        r += b+1;
      }
      for (int i = m; i < n; ++i) {
        v.push_back(range(r, r+b));
        r += b;
      }
      assert(r == this->size());
      return v;
    }

  };

  inline range operator&(const range &a, const range &b) {
    return range::intersection(a,b);
  }

  inline std::ostream& operator<<(std::ostream &os, const range &r) {
    os << *r.begin() << ":" << *r.end();
    return os;
  }


//  struct range_block_iterator
//  : boost::iterator_facade<range_block_iterator,
//    range,
//    boost::random_access_traversal_tag,
//    range // reference
//    >
//  {
//    range_block_iterator(range::iterator it, range r, size_t block) :
//      data_(it), range_(r), block_(block) {}
//
//  private:
//    friend class boost::iterator_core_access;
//
//    void increment() {
//      advance(1);
//    }
//
//    bool equal(const range_block_iterator &it) const {
//      assert(block_ == it.block_);
//      assert(range_ == it.range_);
//      return data_ == it.data_;
//    }
//
//    void advance(ptrdiff_t n) {
//      data_ = std::min(data_ + n*block_, range_.end());;
//    }
//
//    ptrdiff_t distance_to(const range_block_iterator &it) const {
//      assert(this->block_ == it.block_);
//      return (*data_ - *(it.data_))/block_;
//    }
//
//    range dereference() const {
//      range r(*data_, std::min<int>(*data_ + block_, *range_.end()));
//      // std::cout << "*" << r << std::endl;
//      return r;
//    }
//
//  private:
//    range::iterator data_;
//    range range_;
//    size_t block_;
//  };


//  inline
//  iterator_range<range_block_iterator> range::block(size_t N) const {
//    return iterator_range<range_block_iterator>(
//        range_block_iterator(this->begin(), *this, N),
//        range_block_iterator(this->end(), *this, N));
//  }


//  struct range::list {
//     typedef std::vector<range>::const_iterator const_iterator;
//     list() {};
//     size_t rank() const {
//         return data_.size();
//     }
//     const_iterator begin() const { return data_.begin(); }
//     const_iterator end() const { return data_.end(); }
//  private:
//     std::vector<range> data_;
//  };

  inline range rangify1(const range &r) {
    return r;
  }

  template<class R>
  typename madness::enable_if<std::is_integral<R>, range>::type
  rangify1(const R &r) {
    return range(r,r+1);
  }


}

namespace mpqc {

}

namespace sc {
  using mpqc::range;
}

#endif // MPQC_RANGE_HPP
